﻿using System;
using System.Collections.Generic;
using System.ComponentModel;
using System.Drawing;
using System.Data;
using System.Text;
using System.Windows.Forms;
using System.Runtime.InteropServices;
using System.IO;
using System.Collections;
using System.Threading;
using System.Net;
using System.Security;
using System.Security.Cryptography;
using System.Management;
//using mshtml;
using GIMUploaderCommon;
using System.Reflection;

namespace GIMUploader
{
    [Guid("EE1D1E12-E95E-4316-B8B3-DB09302D6F21")]
    public partial class ActiveX : UserControl
    {
        [DllImport("kernel32.dll")]
        public static extern IntPtr _lopen(string lpPathName, int iReadWrite);

        [DllImport("kernel32.dll")]
        public static extern bool CloseHandle(IntPtr hObject);
        public const int OF_READWRITE = 2;
        public const int OF_SHARE_DENY_NONE = 0x40;
        public readonly IntPtr HFILE_ERROR = new IntPtr(-1);


        private string _receiver = "http://localhost/GIMUploaderReceiver.ashx";

        public string Pattern = "*.*";

        public int MaxFileSize = 0;

        public bool MultiFile = true;

        public readonly string version = Config.Version.ToString();
  
         public string Receiver
        {
            get { return this._receiver; }
            set { this._receiver = value; }
        }

        private HtmlDocument _document;
        public HtmlDocument document {
            get { return this._document; }
            set { this._document = value;}
        }

        public ActiveX()
        {
            InitializeComponent();            
        }

        public string UploadSingleFile(string file)
        {
            GIMUploaderFile f = new GIMUploaderFile(file);
 
            if (f.IsExist)
            {
                if (this.MaxFileSize >0  && f.Size > (this.MaxFileSize*1024))
                {
                    f.State = GIMUploaderFileState.Error;
                    f.ErrorInfo = GIMUploaderFileError.OverSize;
                }
                else
                {
                    f.State = GIMUploaderFileState.Uploading;
                    Thread thread = new Thread(this.UploadFileThread);
                    thread.Start(f);
                    GIMUploaderFileList.Save(f);
                }
            }
            else
            {
                f.State = GIMUploaderFileState.Error;
                f.ErrorInfo = GIMUploaderFileError.NotExist;
            }

            return ConvertToJsonString(f);
        }

        public void UploadFile()
        {
            foreach (GIMUploaderFile file in GIMUploaderFileList.Items)
            {
                if (file.IsExist)
                {
                    if (file.State != GIMUploaderFileState.Complated &&file.State != GIMUploaderFileState.Uploading)
                    {
                        file.State = GIMUploaderFileState.Uploading;
                        Thread thread = new Thread(this.UploadFileThread);
                        thread.Start(file);
                        GIMUploaderFileList.Save(file);
                    } 
                }
                else
                {
                    file.State = GIMUploaderFileState.Error;
                    GIMUploaderFileList.Remove(file.Name);
                } 
            }
        }

        public void StopUpload(string f)
        {
            GIMUploaderFile file = GIMUploaderFileList.Get(f);
            if (file != null)
            {
                file.State = GIMUploaderFileState.Stoped;
                GIMUploaderFileList.Save(file); 
            }
        }

        public void StopUploadAll()
        {
            foreach (GIMUploaderFile file in GIMUploaderFileList.Items)
            { 
                file.State = GIMUploaderFileState.Stoped;
                GIMUploaderFileList.Set(file.Name, file);
            }
        }

        public string Add(string file)
        {
            GIMUploaderFile f = new GIMUploaderFile(file);
            
            if (this.MaxFileSize > 0 && f.Size > (this.MaxFileSize * 1024))
            {
                f.State = GIMUploaderFileState.Error;
                f.ErrorInfo = GIMUploaderFileError.OverSize; 
            }
            else
            {
                GIMUploaderFileList.Save(f);
            }

            return ConvertToJsonString(f);
         }

        public string Get(string name)
        {
            if (name == "")
            {
                return "";
            }

            GIMUploaderFile file = GIMUploaderFileList.Get(name);
            if (file != null)
            {
                return ConvertToJsonString(file);
            }
            return "";
        }

        public bool Remove(string file)
        {
            GIMUploaderFile f = GIMUploaderFileList.Get(file);
            if (f != null)
            {
                //正在上传的文件，无法移除，需要先改状态为canceled
                if (f.State == GIMUploaderFileState.Uploading)
                {
                    return false;
                }
                GIMUploaderFileList.Remove(file);
                return true;
            }
            else
            {
                return false;
            } 
        }

        public string ChooseFile() { 
            string filter = BuildFileFilter();
            FileSelector.Filter = filter;
            FileSelector.Multiselect = MultiFile;
            if (FileSelector.ShowDialog() == DialogResult.OK)
            {
                return string.Join("|",FileSelector.FileNames);
            }
            else
            {
                return "";
            }
        }

        public string GetFileContetHash(string fileName)
        {
            try
            {
                MD5 md5 = MD5.Create();
                FileStream fs = File.OpenRead(fileName);
                string hash = BitConverter.ToString(md5.ComputeHash(fs));
                fs.Close();
                return hash.Replace("-","");
            }
            catch {
                return "";
            } 
        }

        private string BuildFileFilter()
        {
            string filter = "所有文件|*.*";
            if (this.Pattern != "*.*")
            {
                filter = "";
                string[] pattern = this.Pattern.Split('|');
                for (int i = 0; i < pattern.Length; i++)
                {
                    if (pattern[i].IndexOf("*.") > -1)
                    {
                        filter += "|" + pattern[i] + "|" + pattern[i];
                    }
                    else
                    {
                        filter += "|*" + pattern[i] + "|*" + pattern[i];
                    }
                }

                if (filter.Length > 0) { filter = filter.Substring(1); }
            }

            return filter;
        }

        private void UploadFileThread(object file)
        {
            int ERRTimes = 0;
            GIMUploaderFile gimFile = (GIMUploaderFile)file;

            string guid = gimFile.GUID;

            if (guid == ""){//文件不存在
                 SetFileState(ref gimFile,GIMUploaderFileState.Error);
                 return;
            }

             IntPtr vHandle = _lopen(gimFile.Name, OF_READWRITE | OF_SHARE_DENY_NONE);
             if (vHandle == HFILE_ERROR)
             {
                 SetFileState(ref gimFile, GIMUploaderFileState.Error);
                 return;
             }

             CloseHandle(vHandle);

            FileStream fs = File.OpenRead(gimFile.Name);
            long startIndex = GetUploadStartIndex(guid);
            gimFile.UploadedSize = startIndex;

            if (startIndex == fs.Length)
            {
                SetFileState(ref gimFile, GIMUploaderFileState.Complated);
                fs.Close();
                return;
            }

            fs.Seek(startIndex, SeekOrigin.Begin);

            int buffer = Config.BufferSize;
            int part = Convert.ToInt32((fs.Length - startIndex) / Convert.ToInt64(buffer));
            int lastbuffer = Convert.ToInt32((fs.Length - startIndex) % Convert.ToInt64(buffer));
            int startbuffer = Convert.ToInt32(startIndex / buffer);
            int i = 0;  

            for (; i < part + 1; i++)
            {

                gimFile = GIMUploaderFileList.Get(gimFile.Name);

                //如果对象已被移除或已取消上传
                if (gimFile == null || gimFile.State== GIMUploaderFileState.Canceled || 
                        gimFile.State== GIMUploaderFileState.Stoped) { fs.Close(); return; }
 
                ERRTimes = 0;
                byte[] data;

                if (i == part)
                {
                    if (lastbuffer > 0)
                        data = new byte[lastbuffer];
                    else 
                        return;
                }
                else
                {
                    data = new byte[buffer];
                }

                fs.Read(data, 0, data.Length);
                while (!PostData(data, startbuffer+i, guid))
                {
                    Thread.Sleep(10);
                    ERRTimes++;
                    if (ERRTimes == 5)
                    {
                        SetFileState(ref gimFile, GIMUploaderFileState.Error);
                        fs.Close();
                        return;
                    }
                }
                gimFile.UploadedSize += data.Length;
                GIMUploaderFileList.Save(gimFile);
            }

            fs.Close();
            SetFileState(ref gimFile, GIMUploaderFileState.Complated);
         }  

        private bool PostData(byte[] data, int i, string guid)
        {
            try
            {
                CommonHelper common = new CommonHelper();
                WebClient request = new WebClient();
                request.Headers.Add(Config.GUIDHeader, guid);

                request.Headers.Add(Config.MD5Header, CommonHelper.GetDataMD5(data));
                request.Headers[Config.CmdHeader] = Config.CMD_UploadData;
                request.Headers[Config.BlockNumberHeader] = i.ToString();
                if (common.GetRequest(this.Receiver, request, data) != Config.Return_OK)
                {
                    return false;
                }

                return true;
            }
            catch (Exception)
            {
                return false;
            }

        } 

        private long GetUploadStartIndex(string guid)
        {
            WebClient request = new WebClient();
            request.Headers.Add(Config.GUIDHeader, guid);
            request.Headers[Config.CmdHeader] = Config.CMD_Hello;
            CommonHelper common = new CommonHelper();
            string ret = common.GetRequest(this.Receiver, request, new byte[1]);

            if (ret != Config.Return_OK)
            {
                return -1;
            }

            else
            {
                return Convert.ToInt64(request.ResponseHeaders[Config.GIMUploadStartHeader]);
            }
        }
 
        private void SetFileState(ref GIMUploaderFile file, GIMUploaderFileState state)
        {
            file.State = state;
            GIMUploaderFileList.Save(file);
        }

        private string ConvertToJsonString(GIMUploaderFile file)
        {
            string ret = @"FileName:'{0}',FileSize:{1},UploadedSize:{2},GUID:'{3}',State:'{4}',ShortName:'{5}',ErrorInfo:'{6}'";
            return "({" + string.Format(ret, file.Name.Replace(@"\", @"\\"), file.Size,
                        file.UploadedSize, file.GUID, file.State.ToString(), file.ShortName,
                        file.ErrorInfo.ToString()) + "})";
        }

        //private void CallJavaScript(string function,object[] args)
        //{ 
        //    Type typeIOleObject = this.GetType().GetInterface("IOleObject", true);
        //    object oleClientSite = typeIOleObject.InvokeMember("GetClientSite", BindingFlags.Instance | BindingFlags.InvokeMethod | BindingFlags.Public,
        //    null, this, null);

        //    IOleClientSite oleClientSite2 = oleClientSite as IOleClientSite;
        //    IOleContainer pObj;
        //    oleClientSite2.GetContainer(out pObj);
 
        //    //获取页面的Script集合
        //    IHTMLDocument pDoc2 = (IHTMLDocument)pObj;
        //    object script = pDoc2.Script;
        //    try
        //    {
        //        //调用JavaScript方法并传递参数，因为此方法可能并没有在页面中实现，所以要进行异常处理
        //        script.GetType().InvokeMember(function, BindingFlags.Instance | BindingFlags.InvokeMethod | BindingFlags.Public,
        //        null, script, args);
        //    }
        //    catch { } 
        //} 
    }

    public static class GIMUploaderFileList
    {
        private static Hashtable fileList = new Hashtable();
        public static GIMUploaderFile Get(string name)
        {
            return (GIMUploaderFile)fileList[name];
        }


        public static bool Add(string name)
        {
            GIMUploaderFile file = new GIMUploaderFile(name);
            if (!fileList.Contains(name))
            {
                fileList.Add(name, file);
            }
            return true;
        }

        public static bool Set(string name, GIMUploaderFile f)
        {
            GIMUploaderFile file = new GIMUploaderFile(name);
            if (fileList.Contains(name))
            {
                fileList[name] = f;
            }
            else
            {
                fileList.Add(name, file);
            }
            return true;
        }

        public static bool Save(GIMUploaderFile file) { 
            return Set(file.Name, file);
        }

        public static bool Exist(string name)
        {
            return fileList.Contains(name);
        }

        public static GIMUploaderFile Remove(string name)
        {
            GIMUploaderFile file = (GIMUploaderFile)fileList[name];
            fileList.Remove(name);
            return file;
        }

        public static bool Clear()
        {
            fileList.Clear();
            return true;
        }

        public static GIMUploaderFile[] Items
        {
            get
            {
                GIMUploaderFile[] items = new GIMUploaderFile[fileList.Count];
                int i = 0;
                foreach (DictionaryEntry item in fileList)
                {
                    items[i] = (GIMUploaderFile)item.Value;
                    i++;
                }

                return items;
            }
        }

        public static int Count
        {
            get { return fileList.Count; }
        }

    }

    public class GIMUploaderFile
    {
        private GIMUploaderFileState _state;

        public GIMUploaderFile(string name)
        {
            FileInfo fi = new FileInfo(name);
            this.Name = name;
            this.ShortName = fi.Name;
            this.Size = fi.Length;
            this.State = GIMUploaderFileState.Added;
            this.IsExist = fi.Exists;
            this.GUID = BuildFileGUID(name);
            
        }

        public string ShortName;
        public string Name;
        public long Size = 0;
        public string Hash;
        public string GUID;
        public long UploadedSize = 0;
        public GIMUploaderFileState State{
            get { return this._state; }
            set {
                this._state = value; 
            }
        } 
        public GIMUploaderFileError ErrorInfo = GIMUploaderFileError.None;
        public bool IsExist; 

        private string BuildFileGUID(string file)
        {
            FileInfo fi = new FileInfo(file);
            if (fi.Exists)
            {
                string guid = fi.Name + fi.LastWriteTime.Ticks.ToString() + CommonHelper.GetMacAddress();
                MD5 md5 = MD5CryptoServiceProvider.Create();

                guid = BitConverter.ToString(md5.ComputeHash(Encoding.ASCII.GetBytes(guid)));
                return guid.Replace("-","");
            }
            else
            {
                return "";
            }
        } 
    }

    public enum GIMUploaderFileState
    {
        Init, Added, Waiting, Uploading, Complated, Error, Canceled, Stoped
    }

    public enum GIMUploaderFileError
    {
        None, OverSize, ErrorType, NotExist
    }

}
